/* Create server-side or client-side for Live Room. */
'use strict';
const assert = require('assert');
const dgram = require('dgram');
const readline = require('readline');
// const fs = require('fs');
const ServerConfig = require('../config/ServerConfig');

function systemNotice(mes) {
  process.stdout.write(`[__NOTICE__]> ${mes}\r\n`);
}

class Server {
  /**
   * Create a server-side.
   * @param {number} port server run port
   * 
   */
  constructor(port) {
    
    this.address = {
      ip: ServerConfig.defaultHostname,
      port: port || ServerConfig.defaultPort
    };

    this.socket = dgram.createSocket('udp4');

    this.ipList = new Map();

    this._init();
  }

  _init() {
    this._listen();
    this._connect();
    this._start();
  }

  // set `listening` event for UDP server
  _listen() {
    this.socket.on('listening', () => {

      // .address() works after .bind()
      const listenUrl = this.socket.address();
      systemNotice(`Listening at ${listenUrl.address}:${listenUrl.port}`);
    });
  }

  // set `message` event for UDP server
  _connect() {
    this.socket.on('message', (msg, rinfo) => {

      let remoteUrl = {
        ip: rinfo.address,
        port: rinfo.port
      },
          remoteUrlStr = JSON.stringify(remoteUrl),

          // token connecting two users
          token = msg.toString();

      // [SYSTEM NOTICE]
      systemNotice(`Get message from ${remoteUrl.ip}:${remoteUrl.port}`);

      if (!this.ipList.has(token)) {
        
        this._registerPartner(remoteUrlStr, token);

        // notice for client
        this.socket.send(`Room created. Please wait your partner to take a part.`, remoteUrl.port, remoteUrl.ip);

      } else {

        this._findPartner(remoteUrlStr, token);

      }

    });
  }

  _registerPartner(remoteUrlStr, token) {
    this.ipList.set(token, remoteUrlStr);
    systemNotice(`IP ${remoteUrlStr} has been saved!`);
  }
  
  _findPartner(remoteUrlStr, token) {
    let savedUrlStr;

    // TODO: what if there are more than two members?
    if (this.ipList.has(token)) {
      savedUrlStr = this.ipList.get(token);
      this._connectPartners(remoteUrlStr, savedUrlStr);
      systemNotice(`Receive the save token \"${token}\", and connect the same token partners`);
    }

  }
  
  _connectPartners(remoteUrlStr, savedUrlStr) {

    // TODO: Error check-in
    let remoteUrl = JSON.parse(remoteUrlStr),
        savedUrl = JSON.parse(savedUrlStr);

    this.socket.send(savedUrlStr, remoteUrl.port, remoteUrl.ip);
    this.socket.send(remoteUrlStr, savedUrl.port, savedUrl.ip);

    // TODO: DELETE the connected token OR DELETE when receive `close` SIGNAL.
  }

  _start() {
    this.socket.bind(this.address.port, this.address.ip);
  }

  _close() {
    console.log(`Service closed.`);
  }
}

module.exports = Server;
